`timescale 1ns/100ps
`default_nettype none
/*************************************************
8 by 8 matrix calculation by using Ct * T to get S
**************************************************/


module SCalc(
	input logic CLOCK_50_I,
	input logic resetn,
	input logic clearResult,
	
	output logic [6:0] DP0_Addr1,
	output logic [6:0] DP1_Addr0,
	input logic [31:0] DP0_Read1,
	input logic [31:0] DP1_Read0,
	
	output logic [6:0] DP1_Addr1,
	output logic [31:0] DP1_Write1,
	output logic DP1_we1,

    // Multiplier
    input logic [31:0] mulResult,           // Result of multiplier
    output logic [31:0] mulOp1,             // Op1 to multiplier
    output logic [31:0] mulOp2,             // Op2 to multiplier
	
	input logic start,
	output logic finish
);

enum logic [4:0]{
	S_IDLE,
	S0,
	S1,
	S2,
	S3,
	S4,
	S5,
	S6,
	S7,
	S8,
	S9,
	S9_WAIT,
	S10,
	S11
}state;
logic [8:0] count;   //9 bits count use to track the address for multiplication
logic [8:0] count_buf;
logic [31:0] acc;    // Accumulator 
logic [6:0] offset;
assign offset = 7'd64;//for bottom half Dual_Port RAM address tracking

always_ff @ (posedge CLOCK_50_I or negedge resetn) begin
    if (resetn == 1'b0) begin
        acc <= 32'd0;
		count <= 9'd0;
		count_buf <= 9'd0;  //use to track multiplication times
		DP0_Addr1 <= offset;
        DP1_Addr0 <= 7'd0;
		DP1_Addr1 <= 7'd0;
		DP1_Write1 <= 32'd0;
        DP1_we1 <= 1'b0;
		finish <= 1'b0;
        state <= S_IDLE;
    end else begin
        case (state)
		S_IDLE:begin
			if (start) begin
				acc <= 32'd0;
				count <= 9'd0;
				count_buf <= 9'd0;
				DP0_Addr1 <= offset;
				DP1_Addr0 <= 7'd0;
				DP1_Addr1 <= 7'd0;
				DP1_Write1 <= 32'd0;
				DP1_we1 <= 1'b0;
				finish <= 1'b0;
				state <= S0;
            end else if (clearResult) begin
				acc <= 32'd0;
                count <= 9'd0;
                count_buf <= 9'd0;
                DP0_Addr1 <= offset;
                DP1_Addr0 <= 7'd0;
				DP1_Addr1 <= 7'd0;
				DP1_Write1 <= 32'd0;
                DP1_we1 <= 1'b0;
                finish <= 1'b0;
			end
		end
		S0: begin //C0 T0
			count <= count +9'd1;				
			DP0_Addr1 <= offset + {1'b0,count[2:0],count[8:6]};
			DP1_Addr0 <= {1'b0,count[2:0],count[5:3]};
            acc <= mulResult;
            finish <= 1'b0;
			DP1_we1 <= 1'b0;
			state <= S1;
		end
        S1: begin//C8 T8
            count <= count +9'd1;				
			DP0_Addr1 <= offset + {1'b0,count[2:0],count[8:6]};
			DP1_Addr0 <= {1'b0,count[2:0],count[5:3]};
			//DP1_we1 <= 1'b0;
			state <= S2;
        end
        S2: begin//C16 T16
            acc <= acc + mulResult; //accumulate the result calculated by Mul_32
			count <= count +9'd1;				
			DP0_Addr1 <= offset + {1'b0,count[2:0],count[8:6]};
			DP1_Addr0 <= {1'b0,count[2:0],count[5:3]};
			DP1_we1 <= 1'b0;
            state <= S3;
        end
        S3: begin//C24 T24
            acc <= acc + mulResult; 
			count <= count +9'd1;				
			DP0_Addr1 <= offset + {1'b0,count[2:0],count[8:6]};
			DP1_Addr0 <= {1'b0,count[2:0],count[5:3]};
			//DP1_we1 <= 1'b0;
            state <= S4;
        end
		S4: begin//C32 T32
            acc <= acc + mulResult; 
			count <= count +9'd1;				
			DP0_Addr1 <= offset + {1'b0,count[2:0],count[8:6]};
			DP1_Addr0 <= {1'b0,count[2:0],count[5:3]};
			state <= S5;
        end
		S5: begin//C40 T40
            acc <= acc + mulResult; 
			count <= count +9'd1;				
			DP0_Addr1 <= offset + {1'b0,count[2:0],count[8:6]};
			DP1_Addr0 <= {1'b0,count[2:0],count[5:3]};
            state <= S6;
        end
		S6: begin//C48 T48
            acc <= acc + mulResult; 
			count <= count +9'd1;				
			count_buf <= count + 9'd1;//buf count, for number of Row*Col calculation
			DP0_Addr1 <= offset + {1'b0,count[2:0],count[8:6]};
			DP1_Addr0 <= {1'b0,count[2:0],count[5:3]};
            state <= S7;
        end
		S7: begin//C52 T56
            acc <= acc + mulResult; 
			count <= count +9'd1;
			DP0_Addr1 <= offset + {1'b0,count[2:0],count[8:6]};
			DP1_Addr0 <= {1'b0,count[2:0],count[5:3]};
            state <= S8;
        end
        S8: begin
            acc <= acc + mulResult;
			state <= S9;
        end
		S9: begin//start to read the first value of Ct and T for the next loop
			acc <= acc + mulResult;
			count <= count + 9'd1;				
			DP0_Addr1 <= offset + {1'b0,count[2:0],count[8:6]};
			DP1_Addr0 <= {1'b0,count[2:0],count[5:3]};
			state <= S9_WAIT;
		end
		S9_WAIT: begin//start to read the second value of Ct and T for the next loop
			count <= count + 9'd1;				
			DP0_Addr1 <= offset + {1'b0,count[2:0],count[8:6]};
			DP1_Addr0 <= {1'b0,count[2:0],count[5:3]};
			//start to load the calculated Ct*T/65536 into DP_RAM1_bottom part
			DP1_Addr1 <= offset + {1'b0,count_buf[8:3]};
			DP1_Write1 <= {{16{acc[31]}}, acc[31:16]};//sign extention
			DP1_we1 <= 1'b1;
			acc <= 32'd0;			
			if(count_buf==9'd511) begin //means one 8*8 matrix 511times calculation is done
				DP1_Addr1 <= offset + {1'b0,count_buf[8:3]};//start to load the last Ct*T/65536 into DP_RAM1_bottom part
				DP1_Write1 <= {{16{acc[31]}}, acc[31:16]};
				DP1_we1 <= 1'b1;
				state <= S10;
			end
			else state <= S2;
		end
		S10: begin//lead out state,provide 2 clk cycles to make the result stable
			DP1_we1 <= 1'b0;
			state <= S11;
		end
		S11: begin
			finish <= 1'b1;
			acc <= 32'd0;
			count <= 9'd0;
			state <= S_IDLE;
		end
        default: state <= S_IDLE;
        endcase
    end
end

/********************************
 * Multiplication block
 ********************************/
always_comb begin
    case(state)
    S_IDLE: begin
        mulOp1 = 32'd0;
        mulOp2 = 32'd0;
    end
	S0: begin
        mulOp1 = 32'd0;
        mulOp2 = 32'd0;
    end
    S1: begin
        mulOp1 = 32'd0;
        mulOp2 = 32'd0;
    end
    S2: begin
        mulOp1 = DP0_Read1;
        mulOp2 = DP1_Read0;
    end
    S3: begin
        mulOp1 = DP0_Read1;
        mulOp2 = DP1_Read0;
    end
	S4: begin
        mulOp1 = DP0_Read1;
        mulOp2 = DP1_Read0;
    end
    S5: begin
        mulOp1 = DP0_Read1;
        mulOp2 = DP1_Read0;
    end
    S6: begin
        mulOp1 = DP0_Read1;
        mulOp2 = DP1_Read0;
    end
    S7: begin
        mulOp1 = DP0_Read1;
        mulOp2 = DP1_Read0;
    end
	S8: begin
		mulOp1 = DP0_Read1;
        mulOp2 = DP1_Read0;
	end
	S9: begin
        mulOp1 = DP0_Read1;
        mulOp2 = DP1_Read0;
    end
	S9_WAIT: begin
        mulOp1 = 32'd0;
        mulOp2 = 32'd0;
    end
	S10: begin
        mulOp1 = 32'd0;
        mulOp2 = 32'd0;
    end
	S11: begin
        mulOp1 = 32'd0;
        mulOp2 = 32'd0;
    end
    default: begin
        mulOp1 = 32'd0;
        mulOp2 = 32'd0;
    end
    endcase
end

endmodule

